/*
 * Copyright (C) 2013 Daniel Wasilew <daniel.wasilew@codeminer.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.codeminer.parts.server.users;

import org.codeminer.parts.application.ApplicationComponent;
import org.codeminer.parts.application.ApplicationContext;
import org.codeminer.parts.application.ShutdownCallback;
import org.codeminer.parts.settings.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 *
 * @author Daniel Wasilew <daniel.wasilew@codeminer.org>
 */
public abstract class AbstractUserManager implements UserManager, ApplicationComponent {

    private static final Logger log = LoggerFactory.getLogger(AbstractUserManager.class);
    private final Map<UUID, User> sessionIds = new HashMap<>();
    private final Map<UUID, Long> lastTimeStamp = new HashMap<>();
    private ApplicationContext context;

    protected final void loggedIN(User user) {
        UUID sessionId = UUID.randomUUID();
        this.sessionIds.put(sessionId, user);
        this.lastTimeStamp.put(sessionId, System.currentTimeMillis());
    }

    @Override
    public final void startup(ApplicationContext context, ShutdownCallback shutdownCallback) throws Exception {
        startUpCalled(context, shutdownCallback);
        this.context = context;
    }

    @Override
    public final void shutdown() {
        shutdownCalled();
    }

    @Override
    public final UUID getSessionId(User user) throws NoSessionException {
        for (Map.Entry<UUID, User> entry : sessionIds.entrySet()) {
            if (entry.getValue().equals(user)) {
                return entry.getKey();
            }
        }

        throw new NoSessionException();
    }

    @Override
    public final User getUser(UUID sessionId) throws NoSessionException, TimeoutException {
        User user = this.sessionIds.get(sessionId);
        if (user == null) {
            throw new NoSessionException();
        } else {
            Long lastSeen = this.lastTimeStamp.get(sessionId);
            long current = System.currentTimeMillis();
            if (current - lastSeen > context.getApplicationPart(Settings.class).getTimout()) {
                throw new TimeoutException();
            }
            this.lastTimeStamp.put(sessionId, current);
            return user;
        }
    }

    @Override
    public final void logout(User user) {
        UUID sessionId = null;
        try {
            sessionId = getSessionId(user);
        } catch (NoSessionException ex) {
            log.trace("could not find session id for user[" + user + "]");
        }

        if (sessionId == null) {
            return; //nothing to do here
        }

        this.sessionIds.remove(sessionId);
        this.lastTimeStamp.remove(sessionId);
    }

    @Override
    public final Class getDescriptionClass() {
        return UserManager.class;
    }

    protected abstract void startUpCalled(ApplicationContext context, ShutdownCallback shutdownCallback);

    protected abstract void shutdownCalled();
}
